package com.xlf.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author xlf
 *
 * 树形结构拼装类
 */
public class TreeUtils {


    /**
     *自定义判断父亲关系方法与拼装children方法
     * @param tree 菜单
     * @param root 根结点
     * @param judge 判断父子关系方法
     * @param <T> 具体的类型
     * @param setChildren 拼接父子方法
     * @return
     */
    public static <T> T getTree(List<T> tree, T root, BiPredicate<T,T> judge, BiConsumer<T,List<T>> setChildren)  {
        ArrayList<T> children = new ArrayList<>();
        for (int i=0;i<tree.size();i++)
        {
            T v = tree.get(i);
            if(judge.test(root,v))
            {
                children.add(getTree(tree,v,judge,setChildren));
            }
        }
        setChildren.accept(root,children);
        return root;
    }

    /**
     * 传入菜单，返回树形结构，菜单类需要有id，parent,children属性，根结点id为0
     * @param tree
     * @param <T>
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     *
     */
    public static <T> List<T> buildTree(List<T> tree) throws InvocationTargetException,
            IllegalAccessException,
            NoSuchMethodException {
        ArrayList<T> res = new ArrayList<>();
        for(int i=0;i<tree.size();i++){
            T root = tree.get(i);
            Method getParent = root.getClass().getMethod("getParentId");
            Long parentId = (Long)getParent.invoke(root);
            //判断是否是根节点
            if(parentId!=null&&parentId.equals(0L)){
                //递归拼装子结点
                res.add(getTree(tree,root));
            }
        }
        return res;
    }

    public static <T> List<T> buildTree2(List<T> tree) throws InvocationTargetException,
            IllegalAccessException,
            NoSuchMethodException {
        ArrayList<T> res = new ArrayList<>();
        Set<T> processedNodes = new HashSet<>(); // 用于记录已经处理过的节点

        for (int i = 0; i < tree.size(); i++) {
            T root = tree.get(i);
            Method getParent = root.getClass().getMethod("getParentId");
            Long parentId = (Long) getParent.invoke(root);

            // 判断是否是根节点
            if (parentId != null && parentId.equals(0L)) {
                // 递归拼装子结点
                res.add(getTree2(tree, root, processedNodes));
            }
        }
        return res;
    }

    private static <T> T getTree2(List<T> tree, T root, Set<T> processedNodes) throws InvocationTargetException,
            IllegalAccessException,
            NoSuchMethodException {
        if (processedNodes.contains(root)) {
            return root; // 如果节点已经处理过，直接返回
        }

        processedNodes.add(root); // 标记当前节点为已处理

        Method getId = root.getClass().getMethod("getId");
        Long rootId = (Long) getId.invoke(root);

        Method getChildren = root.getClass().getMethod("getChildren");
        List<T> children = (List<T>) getChildren.invoke(root);

        for (int i = 0; i < tree.size(); i++) {
            T node = tree.get(i);
            Method getParent = node.getClass().getMethod("getParentId");
            Long parentId = (Long) getParent.invoke(node);

            if (parentId != null && parentId.equals(rootId)) {
                children.add(getTree2(tree, node, processedNodes));
            }
        }

        return root;
    }

    /**
     *
     * @param tree 操作对象列表
     * @param getId 指明Id字段
     * @param getChildrenId 指名指向父Id字段
     * @param value 根结点的值
     * @param setChildren 设置孩子列表
     * @param <E> id
     * @param <T> 操作对象
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     */
    public static <E,T> List<T> buildTree(List<T> tree, Function<T,E> getId, Function<T,E> getChildrenId, E value, BiConsumer<T,List<T>> setChildren) {
    ArrayList<T> res = new ArrayList<>();
        for(int i=0;i<tree.size();i++){
            T root = tree.get(i);
            E id = getChildrenId.apply(root);
            //判断是否是根节点
            if(id!=null&&id.equals(value)){
                //递归拼装子结点
                res.add(getTree(tree,root,getId,getChildrenId,setChildren));
            }
        }
        return res;
    }

    /**
     *
     * @param tree 操作对象列表
     * @param parent 父亲
     * @param getId  指明Id字段
     * @param getChildrenId 指名指向父Id字段
     * @param setChildren 设置孩子列表
     * @param <E> id
     * @param <T> 操作对象
     * @return
     */
    public static <E,T> T getTree(List<T> tree, T parent, Function<T,E>getId, Function<T,E> getChildrenId, BiConsumer<T,List<T>> setChildren) {
        ArrayList<T> children = new ArrayList<>();
        E parentId = getId.apply(parent);
        for (int i=0;i<tree.size();i++)
        {
            T child = tree.get(i);
            E id = getChildrenId.apply(child);
            if(id!=null&&parentId.equals(id))
            {
                children.add(getTree(tree,child,getId,getChildrenId,setChildren));
            }
        }
        setChildren.accept(parent,children);
        return parent;
    }

    public static <T> T getTree(List<T> tree, T root) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {
        ArrayList<T> children = new ArrayList<>();
        for (int i=0;i<tree.size();i++)
        {
            T v = tree.get(i);
            if(judge(root,v))
            {
                children.add(getTree(tree,v));
            }
        }
        root.getClass().getMethod("setChildren",List.class).invoke(root,children);
        return root;
    }

    /**
     * 判断是否是父子关系
     * @param root
     * @param children
     * @param <T>
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private static <T> boolean judge(T root, T children) throws NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        Long id = (Long) root.getClass().getMethod("getId").invoke(root);
        Long parentId = (Long)children.getClass().getMethod("getParentId").invoke(children);
        if(parentId==null||id==null)
            return false;
        return id.equals(parentId);

    }
}
